package com.weijianhuawen.wjblog.admin.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.weijianhuawen.wjblog.admin.convert.CommentConvert;
import com.weijianhuawen.wjblog.admin.event.PublishArticleEvent;
import com.weijianhuawen.wjblog.admin.event.PublishCommentEvent;
import com.weijianhuawen.wjblog.admin.event.UpdateCommentEvent;
import com.weijianhuawen.wjblog.admin.mode.vo.comment.*;
import com.weijianhuawen.wjblog.admin.service.AdminCommentService;
import com.weijianhuawen.wjblog.common.domain.dos.BlogSettingsDo;
import com.weijianhuawen.wjblog.common.domain.dos.CommentDo;
import com.weijianhuawen.wjblog.common.domain.dos.UserDetailDo;
import com.weijianhuawen.wjblog.common.domain.dos.UserDo;
import com.weijianhuawen.wjblog.common.domain.mapper.BlogSettingsMapper;
import com.weijianhuawen.wjblog.common.domain.mapper.CommentMapper;
import com.weijianhuawen.wjblog.common.domain.mapper.UserDetailMapper;
import com.weijianhuawen.wjblog.common.domain.mapper.UserMapper;
import com.weijianhuawen.wjblog.common.enums.CommentStatusEnum;
import com.weijianhuawen.wjblog.common.enums.ResponseCodeEnum;
import com.weijianhuawen.wjblog.common.exception.BizException;
import com.weijianhuawen.wjblog.common.mail.MailHelper;
import com.weijianhuawen.wjblog.common.utils.PageResponse;
import com.weijianhuawen.wjblog.common.utils.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import toolgood.words.IllegalWordsSearch;
import toolgood.words.IllegalWordsSearchResult;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @version: java version 8
 * @Author: weijianhuawen
 * @description:
 * @date: 2024-12-21 23:15
 */

@Service
@Slf4j
public class AdminCommentServiceImpl implements AdminCommentService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private UserDetailMapper userDetailMapper;
    @Resource
    private BlogSettingsMapper blogSettingsMapper;
    @Resource
    private CommentMapper commentMapper;
    @Autowired
    private IllegalWordsSearch illegalWordsSearch;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Override
    public Response publishComment(PublishCommentReqVo vo) {
        // 获取登录用户用户名
        String username = getUserUsername();
        if (StringUtils.isBlank(username)) throw new BizException(ResponseCodeEnum.USER_NOT_LOGIN);

        // 查询用户是否存在
        UserDo userDo = userMapper.findByUsername(username);
        if (Objects.isNull(userDo)) throw new BizException(ResponseCodeEnum.USERNAME_NOT_FOUND);

        // 获取用户id
        Long userId = userDo.getId();
        // 查询用户详细信息
        UserDetailDo userDetailDo = userDetailMapper.selectUserDetailByUserId(userId);
        if (Objects.isNull(userDetailDo)) throw new BizException(ResponseCodeEnum.USER_DETAIL_NOT_EXIST);

        // 获取昵称，邮箱和头像
        String nickname = userDetailDo.getNickname();
        String mail = userDetailDo.getMail();
        String avatar = userDetailDo.getAvatar();
        // 获取请求信息
        String content = vo.getContent();
        Long replyCommentId = vo.getReplyCommentId();

        // 检查邮箱格式是否正确
        if (!MailHelper.validateEmail(mail)) throw new BizException(ResponseCodeEnum.USER_MAIL_FORMAT_ERROR);

        // 查询博客相关设置信息
        BlogSettingsDo blogSettingsDo = blogSettingsMapper.selectById(1L);
        // 是否开启敏感词过滤
        Boolean isCommentSensiWordOpen = blogSettingsDo.getIsCommentSensiWordOpen();
        // 是否开启了评论审核
        Boolean isCommentExamineOpen = blogSettingsDo.getIsCommentExamineOpen();

        // 设置默认状态
        Integer status = CommentStatusEnum.NORMAL.getCode();
        // 审核不通过原因
        String reason  = "";

        // 如果开启审核，则将评论状态设置为待审核状态，等待管理员审核
        if (isCommentExamineOpen) {
            status = CommentStatusEnum.WAIT_EXAMINE.getCode();
        }
        // 评论是否含有敏感词
        Boolean isContainSensitiveWord = false;
        // 是否开启敏感词过滤
        if (isCommentSensiWordOpen) {
            // 开启，则进行敏感词检测
            // 校验评论内容是否出现敏感词
            isContainSensitiveWord = illegalWordsSearch.ContainsAny(content);
            if (isContainSensitiveWord) {
                // 包含敏感词，则将评论状态设置为审核不通过
                status = CommentStatusEnum.EXAMINE_FAILED.getCode();
                // 匹配到所有敏感词词组
                List<IllegalWordsSearchResult> results = illegalWordsSearch.FindAll(content);
                List<String> keyWords = results.stream().map(result -> result.Keyword).collect(Collectors.toList());
                // 生成不通过原因
                reason = String.format("系统检测您的评论存在非法言语，例如：%s", keyWords);
                log.warn("【评论服务模块】 检测到敏感词：{}，原评论为：{}", keyWords, content);
            }
        }

        // 构建评论
        CommentDo commentDo = CommentDo.builder()
                .content(content)
                .nickname(nickname)
                .avatar(avatar)
                .mail(mail)
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .routerUrl(vo.getRouterUrl())
                .website(vo.getWebsite())
                .replyCommentId(replyCommentId)
                .parentCommentId(vo.getParentCommentId())
                .status(status)
                .reason(reason)
                .userId(userId)
                .build();
        // 新增评论
        commentMapper.insert(commentDo);
        // 添加发布评论事件
        Long commentId = commentDo.getId();
        eventPublisher.publishEvent(new PublishCommentEvent(this, commentId, userId));
        // 如果存在敏感词，返回信息到前端
        if (isContainSensitiveWord) {
            throw new BizException(ResponseCodeEnum.COMMENT_CONTAIN_SENSITIVE_WORD);
        }

        if (Objects.equals(status, CommentStatusEnum.WAIT_EXAMINE.getCode())) {
            // 评论成功，告知用户评论正在审核中
//            throw new BizException(ResponseCodeEnum.COMMENT_WAIT_EXAMINE);
            return Response.fail(ResponseCodeEnum.COMMENT_WAIT_EXAMINE);
        }
        return Response.success();
    }

    @Override
    public PageResponse findCommentPageList(FindCommentPageListReqVo vo) {
        // 获取查询页数页页码
        Long size = vo.getSize();
        Long current = vo.getCurrent();

        // 获取查询日期区间
        LocalDate startDate = vo.getStartDate();
        LocalDate endDate = vo.getEndDate();
        // 获取查询评论状态
        Integer status = vo.getStatus();
        // 获取查询路由
        String commentUrl = vo.getRouterUrl();

        // 分页查询
        Page<CommentDo> page = commentMapper.selectPageList(current, size, commentUrl, startDate, endDate, status);
        // 获取分页数据
        List<CommentDo> commentDos = page.getRecords();
        List<FindCommentPageListRspVo> vos = Lists.newArrayList();
        // 将Do转换为vo
        if (!CollectionUtils.isEmpty(commentDos)) {
            vos = commentDos.stream().map(commentDo -> CommentConvert.INSTANCE.do2PageListVo(commentDo))
                    .collect(Collectors.toList());

        }

        return PageResponse.success(page, vos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteComment(DeleteCommentReqVo vo) {
        // 获取需要删除的id
        Long id = vo.getId();

        // 查询该评论是否存在
        CommentDo commentDo = commentMapper.selectById(id);

        if (Objects.isNull(commentDo)) {
            log.warn("【后台评论服务】 id为{}的评论不存在！", id);
            throw new BizException(ResponseCodeEnum.COMMENT_NOT_ESIST);
        }

        // 获取评论是一级评论还是二级评论
        boolean isOneLevel = Objects.isNull(commentDo.getReplyCommentId()) && Objects.isNull(commentDo.getParentCommentId());

        // 删除评论本身
        commentMapper.deleteById(id);

        // 判断
        if (isOneLevel) {
            // 删除子评论
            commentMapper.deleteByParentCommentId(id);
        } else {
            // 删除所有回复该评论的评论
            deleteAllReplyComment(id);
        }
        return Response.success();
    }

    @Override
    public Response examineComment(ExamineCommentReqVo vo) {
        // 获取审核数据
        Long id = vo.getId();
        String reason = vo.getReason();
        Integer status = vo.getStatus();

        // 查询该评论
        CommentDo commentDo = commentMapper.selectById(id);

        if (Objects.isNull(commentDo)) {
            log.warn("【后台评论服务】 审核评论id{}不存在或已删除", id);
            throw new BizException(ResponseCodeEnum.COMMENT_NOT_ESIST);
        }
        // 获取评论状态
        Integer currCommentStatus = commentDo.getStatus();
        // 更新评论
        commentMapper.updateById(CommentDo.builder()
                        .id(commentDo.getId())
                        .status(status)
                        .reason(reason)
                        .updateTime(LocalDateTime.now())
                .build());

        // 如果评论为待审核状态，发布审核评论事件
        if (Objects.equals(currCommentStatus, CommentStatusEnum.WAIT_EXAMINE.getCode())) {
            eventPublisher.publishEvent(new UpdateCommentEvent(this, id, commentDo.getUserId()));
        }
        return Response.success();
    }

    // 删除所有回复该评论的评论
    private void deleteAllReplyComment(Long commentId) {
        // 查询该评论的所有回复
        List<CommentDo> replyComments = commentMapper.selectByReplyCommentId(commentId);
        // 如果回复评论列表为空，则返回
        if (Objects.isNull(replyComments)) return;

        // 遍历所有评论，并递归删除回复评论的回复评论
        replyComments.forEach(commentDo -> {
            Long childCommentId = commentDo.getId();
            // 删除回复子评论
            commentMapper.deleteById(childCommentId);
            // 删除回复评论的回复子评论
            deleteAllReplyComment(childCommentId);
        });
    }

    // 获取登录用户的用户名
    private String getUserUsername() {
        // 获取储存在ThreadLocal中的用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return authentication.getName();
    }
}
